package virtualstock.request;

import java.math.BigDecimal;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;

import javax.ejb.EJBException;
import javax.ejb.Stateful;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;

import virtualstock.entity.Portfolio;
import virtualstock.entity.Stock;
import virtualstock.entity.Transaction;
import virtualstock.exception.InsufficientCreditException;
import virtualstock.exception.InsufficientShareException;
import virtualstock.exception.InvalidParameterException;
import virtualstock.exception.PortfolioNotFoundException;
import virtualstock.exception.StockNotFoundException;
import virtualstock.exception.TransactionNotFoundException;
import virtualstock.util.TransactionDetails;

/**
 * Session Bean implementation class TransactionControllerBean
 */
@Stateful
public class TransactionControllerBean implements TransactionController {
	private BigDecimal bigZero = new BigDecimal("0.00");
	@PersistenceContext
	private EntityManager em;
//	@PersistenceUnit
//	private EntityManagerFactory emf;

    /**
     * Default constructor. 
     */
    public TransactionControllerBean() {
        // TODO Auto-generated constructor stub
    }

	
	public void buy(Long portfolioID, Long stockID, int share, BigDecimal currentPrice) 
		throws InvalidParameterException, PortfolioNotFoundException, StockNotFoundException, InsufficientCreditException {
		// TODO Auto-generated method stub
		
		Portfolio portfolio = null;
		Stock stock = null;
		Transaction transaction = null;
		
		// add some checkings
		if (portfolioID == null) {
			throw new InvalidParameterException("null portfolioID");
		}
		if (stockID == null) {
			throw new InvalidParameterException("null stockID");
		}
		if (share < 0) {
			throw new InvalidParameterException("share is negative");
		}
		if (currentPrice == null) {
			throw new InvalidParameterException("null currentPrice");
		}
		if (currentPrice.compareTo(bigZero) != 1) {	// when price is negative and zero
			throw new InvalidParameterException("currentPrice is negative");
		}
		
		try {
			portfolio = em.find(Portfolio.class, portfolioID);
		} catch (Exception ex) {
			throw new PortfolioNotFoundException(portfolioID);
		}
		
		try {
			stock = em.find(Stock.class, stockID);
		} catch (Exception ex) {
			throw new StockNotFoundException(stockID);
		}
		
		BigDecimal newBalance = 
			portfolio.getBalance().subtract(currentPrice.multiply(new BigDecimal(share)));
		if (newBalance.compareTo(bigZero) == -1) {
			System.err.println("InsufficientCreditException");
			throw new InsufficientCreditException();
		} else {
			String type = "buy";
			try {
				portfolio.setBalance(newBalance);
				transaction = new Transaction(
						portfolio,
						type,
						stock,
						share,
						currentPrice);
				em.persist(transaction);
			} catch (Exception ex) {
				throw new EJBException(ex.getMessage());
			}
			//executeTransaction(portfolio, type, stock, share, currentPrice, newBalance);
		}
	}

	
	public void sell(Long portfolioID, Long stockID, int share, BigDecimal currentPrice) 
		throws InvalidParameterException, PortfolioNotFoundException, StockNotFoundException, InsufficientShareException {
		// TODO Auto-generated method stub

		Portfolio portfolio = null;
		Stock stock = null;
		Transaction transaction = null;
		
		// add some checkings
		if (portfolioID == null) {
			throw new InvalidParameterException("null portfolioID");
		}
		if (stockID == null) {
			throw new InvalidParameterException("null stockID");
		}
		if (share < 0) {
			throw new InvalidParameterException("share is negative");
		}
		if (currentPrice == null) {
			throw new InvalidParameterException("null currentPrice");
		}
		if (currentPrice.compareTo(bigZero) == -1) {
			throw new InvalidParameterException("currentPrice is negative");
		}
		
		try {
			portfolio = em.find(Portfolio.class, portfolioID);
		} catch (Exception ex) {
			throw new PortfolioNotFoundException(portfolioID);
		}
		
		try {
			stock = em.find(Stock.class, stockID);
		} catch (Exception ex) {
			throw new StockNotFoundException(stockID);
		}
		
		int currentShare = getShareInPortfolio(portfolioID, stockID);
		if (currentShare < share) {	// if current share is not enough to sell
			throw new InsufficientShareException();
		} else {
			String type = "sell";
			BigDecimal newBalance = 
				portfolio.getBalance().add(currentPrice.multiply(new BigDecimal(share)));
			try {
				portfolio.setBalance(newBalance);
				transaction = new Transaction(
						portfolio,
						type,
						stock,
						share,
						currentPrice);
				em.persist(transaction);
			} catch (Exception ex) {
				throw new EJBException(ex.getMessage());
			}
			//executeTransaction(portfolio, type, stock, share, currentPrice, newBalance);
		}
	}

	
	public TransactionDetails getDetails(Long transactionID)
			throws InvalidParameterException, TransactionNotFoundException {
		// TODO Auto-generated method stub
		
		TransactionDetails details = null;
		Transaction transaction = null;
		
		if (transactionID == null) {
			throw new InvalidParameterException("null transactionID");
		}
		
		try {
			transaction = em.find(Transaction.class, transactionID);
			
			// why details use string for portolio and stock???
			details = new TransactionDetails(
					transaction.getTransactionID(),
					transaction.getType(),
					transaction.getStock().getStockID().toString(),
					transaction.getShare(),
					transaction.getCurrentPrice(),
					transaction.getTimestamp());
		} catch (Exception ex) {
			throw new TransactionNotFoundException(transactionID);
		}
		
		return details;
	}
	

	
	public int getShareInPortfolio(Long portfolioID, Long stockID)
			throws InvalidParameterException, PortfolioNotFoundException, StockNotFoundException {
		
		int share = 0;
		
		if (portfolioID == null) {
			throw new InvalidParameterException("null portfolioID");
		}
		if (stockID == null) {
			throw new InvalidParameterException("null stockID");
		}
		
		Portfolio portfolio = null;
		try {
			portfolio = em.find(Portfolio.class, portfolioID);
		} catch (Exception ex) {
			throw new EJBException(ex.getMessage());
		}
		
		Stock stock = null;
		try {
			stock = em.find(Stock.class, stockID);
		} catch (Exception ex) {
			throw new EJBException(ex.getMessage());
		}
		
		if (portfolio == null) {
			throw new PortfolioNotFoundException(portfolioID);
		}
		if (stock == null) {
			throw new StockNotFoundException(stockID);
		}
		
		try {
			int shareBuy, shareSell;
			
			Object shareBuyObject = em.createNamedQuery("Transaction.FindBuySharesInPortfolioByStockID")
				.setParameter("portfolioID", portfolio)
				.setParameter("stockID", stock)
				.getSingleResult();
			
			Object shareSellObject = em.createNamedQuery("Transaction.FindSellSharesInPortfolioByStockID")
				.setParameter("portfolioID", portfolio)
				.setParameter("stockID", stock)
				.getSingleResult();
			
			if (shareBuyObject == null) {
				shareBuy = (new Long(0)).intValue();
			} else {
				shareBuy = (new Long(shareBuyObject.toString())).intValue();
			}
			
			if (shareSellObject == null) {
				shareSell = (new Long(0)).intValue();
			} else {
				shareSell = (new Long(shareSellObject.toString())).intValue();
			}
			
			share = shareBuy - shareSell;
			
		} catch (Exception ex) {
			throw new EJBException(ex.getMessage());
		}
		
		return share;
	}
	
/*	private void executeTransaction(
			Portfolio portfolio,
			String type,
			Stock stock, 
			int share,
			BigDecimal currentPrice,
			BigDecimal newBalance) {
		
		Transaction transaction = null;
		
		try {
			EntityManager emTmp = emf.createEntityManager();
//			emTmp.getTransaction().begin();
//			Context ic = new InitialContext();
//			UserTransaction ut =
//				(UserTransaction) ic.lookup("java:comp/UserTransaction");
//			(UserTransaction) ic.lookup("java:comp/UserTransaction");
//			ut.begin();


//			em.getTransaction().begin();
			portfolio.setBalance(newBalance);
			transaction = new Transaction(
					portfolio,
					type,
					stock,
					share,
					currentPrice);
			em.persist(transaction);
//			emTmp.getTransaction().commit();
//			ut.commit();
//			em.getTransaction().commit();
		} catch (Exception ex) {
			throw new EJBException("executeTransaction: " + ex.getMessage());
		}
	}
*/
	public HashMap<Long, Integer> getStocksInPortfolio(Long portfolioID) 
		throws InvalidParameterException, PortfolioNotFoundException, StockNotFoundException {
		// TODO Auto-generated method stub
		
		HashMap<Long, Integer> stockShareMap = new HashMap<Long, Integer>();
		
		if (portfolioID == null) {
			throw new InvalidParameterException("null portfolioID");
		}
		
		Portfolio portfolio = null;
		try {
			portfolio = em.find(Portfolio.class, portfolioID);
		} catch (Exception ex) {
			throw new EJBException(ex.getMessage());
		}
		
		try {
			List<Stock> stocks = null;
			stocks = em.createNamedQuery("Transaction.FindStocksInPortfolio")
				.setParameter("portfolio", portfolio)
				.getResultList();
			
			for (Iterator<Stock> it = stocks.iterator(); it.hasNext(); ) {
				Stock stock = it.next();
				System.out.println("to process stock " + stock.getStockID());
				int share = getShareInPortfolio(portfolioID, stock.getStockID());
				stockShareMap.put(stock.getStockID(), share);
				
			}
		} catch (Exception ex) {
			throw new EJBException(ex.getMessage());
		}
		
		return stockShareMap;
	}


}
